﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using SharePointUtilitiesForDevelopers.Repository;

namespace SharePointUtilitiesForDevelopers.Actions
{
    /// <summary>
    /// A form to build the view fields XML.
    /// </summary>
    public partial class BuildViewFieldsForm : Form
    {
        #region Fields

        private readonly ISharePointListRepository _listRepository;

        #endregion Fields

        #region Properties

        /// <summary>
        /// Gets or sets the list.
        /// </summary>
        /// <value>The list.</value>
        public SharePointList List { get; set; }

        /// <summary>
        /// Gets or sets the web.
        /// </summary>
        /// <value>The web.</value>
        public SharePointWeb Web { get; set; }

        /// <summary>
        /// Gets or sets the view fields XML.
        /// </summary>
        /// <value>The view fields XML definition.</value>
        public XElement ViewFieldsDefinition { get; set; }

        #endregion Properties

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BuildViewFieldsForm"/> class.
        /// </summary>
        public BuildViewFieldsForm()
        {
            InitializeComponent();

            // Get the dependencies
            _listRepository = ServiceLocator.Resolve<ISharePointListRepository>();

        }

        #endregion Constructors

        #region Event Handlers

        /// <summary>
        /// Handles the Load event of the BuildViewFieldsForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BuildViewFieldsForm_Load(object sender, EventArgs e)
        {
            // Bind the list fields
            BindViewFields(ViewFieldsDefinition);

            // Bind the view fields xml
            BindViewFieldXml(ViewFieldsDefinition);
        }

        /// <summary>
        /// Handles the DoubleClick event of the availableFieldsListBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void availableFieldsListBox_DoubleClick(object sender, EventArgs e)
        {
            // Move selected items
            MoveSelectedItems(availableFieldsListBox, selectedFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();
        }

        /// <summary>
        /// Handles the DoubleClick event of the selectedFieldsListBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void selectedFieldsListBox_DoubleClick(object sender, EventArgs e)
        {
            // Move selected items
            MoveSelectedItems(selectedFieldsListBox, availableFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();
        }

        /// <summary>
        /// Handles the Click event of the addButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void addButton_Click(object sender, EventArgs e)
        {

            // Move selected items
            MoveSelectedItems(availableFieldsListBox, selectedFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();
        }

        /// <summary>
        /// Handles the Click event of the removeButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeButton_Click(object sender, EventArgs e)
        {
            // Move selected items
            MoveSelectedItems(selectedFieldsListBox, availableFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();

        }

        /// <summary>
        /// Handles the Click event of the addAllButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void addAllButton_Click(object sender, EventArgs e)
        {
            // Move all items
            MoveAllItems(availableFieldsListBox, selectedFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();

        }

        /// <summary>
        /// Handles the Click event of the removeAllButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeAllButton_Click(object sender, EventArgs e)
        {
            // Move all items
            MoveAllItems(selectedFieldsListBox, availableFieldsListBox);

            // Update xml
            UpdateListFieldXmlTextFromSelected();
        }

        /// <summary>
        /// Handles the Click event of the cancelButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cancelButton_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
            Close();
        }

        /// <summary>
        /// Handles the Click event of the okButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void okButton_Click(object sender, EventArgs e)
        {
            // Validate the xml
            if (!ValidateViewFieldsXml(false))
            {
                return;
            }

            // Set the view fields xml
            ViewFieldsDefinition = GetViewFieldsTextAsXml();

            // Close the form
            DialogResult = DialogResult.OK;
            Close();
        }

        /// <summary>
        /// Handles the Validating event of the viewFieldsXmlTextBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void viewFieldsXmlTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = !ValidateViewFieldsXml(false);
        }

        /// <summary>
        /// Handles the Validated event of the viewFieldsXmlTextBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void viewFieldsXmlTextBox_Validated(object sender, EventArgs e)
        {
            // Parse the xml
            var viewFieldsDefinition =
                SharePointFieldExtensionMethods.ToViewFieldsXElement((string) viewFieldsXmlTextBox.Text, List.Fields);

            // Update the xml
            viewFieldsXmlTextBox.Text = viewFieldsDefinition.ToString();

            // Update the fields based on the xml
            BindViewFields(viewFieldsDefinition);
        }

        /// <summary>
        /// Handles the LinkClicked event of the helpLink control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.LinkLabelLinkClickedEventArgs"/> instance containing the event data.</param>
        private void helpLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Help.ShowHelp(this, string.Format(Properties.Resources.HelpSearchUrl, Properties.Resources.HelpKeywordViewFields));
        }

        /// <summary>
        /// Handles the HelpRequested event of the BuildViewFieldsForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="hlpevent">The <see cref="System.Windows.Forms.HelpEventArgs"/> instance containing the event data.</param>
        private void BuildViewFieldsForm_HelpRequested(object sender, HelpEventArgs hlpevent)
        {
            Help.ShowHelp(this, string.Format(Properties.Resources.HelpSearchUrl, Properties.Resources.HelpKeywordViewFields));
        }

        #endregion Event Handlers

        #region Helper Methods
        
        /// <summary>
        /// Gets the view fields.
        /// </summary>
        /// <returns></returns>
        private XElement GetViewFieldsTextAsXml()
        {
            if (!string.IsNullOrEmpty(viewFieldsXmlTextBox.Text))
            {
                return XElement.Parse(viewFieldsXmlTextBox.Text);
            }
            return null;
        }

        /// <summary>
        /// Validates the view fields XML.
        /// </summary>
        /// <param name="showMessageIfValid">Shows a message if the xml is valid.</param>
        private bool ValidateViewFieldsXml(bool showMessageIfValid)
        {
            try
            {
                GetViewFieldsTextAsXml();

                if (showMessageIfValid)
                {
                    MessageBox.Show(
                        this, 
                        Properties.Resources.ValidationViewFieldsFormatValid,
                        Properties.Resources.ValidationViewFieldsCaption, 
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                }

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    this, 
                    string.Format(Properties.Resources.ValidationViewFieldsFormat, ex.Message), 
                    Properties.Resources.ValidationViewFieldsCaption, 
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return false;
            }
        }

        /// <summary>
        /// Binds the list field definition.
        /// </summary>
        /// <param name="viewFieldsDefinition">The view fields xml definition.</param>
        private void BindViewFieldXml(XElement viewFieldsDefinition)
        {
            viewFieldsXmlTextBox.Text = string.Empty;
            if (viewFieldsDefinition != null)
            {
                viewFieldsXmlTextBox.Text = viewFieldsDefinition.ToString();
            }
        }

        /// <summary>
        /// Updates the list field XML text from selected.
        /// </summary>
        private void UpdateListFieldXmlTextFromSelected()
        {
            viewFieldsXmlTextBox.Text = Enumerable.Cast<string>(selectedFieldsListBox.Items).ToViewFieldsXElement().ToString();
        }

        /// <summary>
        /// Binds the view fields.
        /// </summary>
        /// <param name="viewFieldsDefinition">The view fields definition.</param>
        private void BindViewFields(XElement viewFieldsDefinition)
        {
            // Clear fields
            availableFieldsListBox.Items.Clear();
            selectedFieldsListBox.Items.Clear();

            // Load fields on demand
            if (List.Fields == null)
            {
                _listRepository.LoadListFields(List, Web.Url);
            }

            if (List.Fields != null)
            {
                // Get the fields to display
                var selectedFields = new List<string>();
                if (viewFieldsDefinition != null)
                {
                    // Get only the fields that are in the selected field
                    selectedFields = (from fldName in viewFieldsDefinition.ToViewFieldList()
                                      //where List.Fields.FindByStaticName(fldName) != null // <-- This will only allow fields that are in the list fields
                                      select fldName).ToList();
    
                }

                // Get the available fields (not in the selected fields)
                var availableFields =
                    (from fld in List.Fields
                     where ((!fld.Hidden) && (!selectedFields.Contains(fld.StaticName)))
                     orderby fld.StaticName
                     select fld.StaticName).ToList();
                
                // Bind available fields
                availableFieldsListBox.BeginUpdate();
                foreach (var fieldName in availableFields)
                {
                    availableFieldsListBox.Items.Add(fieldName);
                }
                availableFieldsListBox.EndUpdate();

                // Bind selected fields
                selectedFieldsListBox.BeginUpdate();
                foreach (var fieldName in selectedFields)
                {
                    selectedFieldsListBox.Items.Add(fieldName);
                } 
                selectedFieldsListBox.EndUpdate();
            }
        }

        /// <summary>
        /// Moves the selected items.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        private static void MoveSelectedItems(ListBox from, ListBox to)
        {
            var items = new List<object>();
            
            // Get the list of items to move
            foreach(var item in from.SelectedItems)
            {
                items.Add(item);
            }

            // Move
            foreach(var item in items)
            {
                to.Items.Add(item);
                from.Items.Remove(item);
            }
            
        }

        /// <summary>
        /// Moves all items.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        private static void MoveAllItems(ListBox from, ListBox to)
        {
            var items = new List<object>();

            // Get the list of items to move
            foreach (var item in from.Items)
            {
                items.Add(item);
            }

            // Move
            foreach (var item in items)
            {
                to.Items.Add(item);
                from.Items.Remove(item);
            }
        }

        #endregion Helper Methods
       
    }
}